<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>[1.22]-签名验证：自定义签名规则 | PhalApi(π框架) - PHP轻量级开源接口框架 - 助你创造价值！</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <meta name="Author" content="PhalApi,ecitlm,dogstar">
    <meta name="description" content="[1.22]-签名验证：自定义签名规则，PhalApi是一个PHP轻量级开源接口框架，致力于快速开发接口服务。支持HTTP/SOAP/RPC等协议，可用于搭建接口/微服务/RESTful接口/Web Services。我们不断更新，保持生气；为接口负责，为开源负责！并承诺永久免费！">
    <meta name="keywords" content="PhalApi,phalapi,phalapi接口开发,后台接口开发,后台接口开发框架,接口开发,接口框架,开源接口框架,PHP后台接口开发,PHP接口开发,PHP接口框架,PHP后台接口框架,phalapi官网,PHP接口框架,php接口开发框架,php接口开发,php web框架,π框架,π开发框架,π接口框架,π接口开发框架,api接口,php接口,,PHP接口框架,phalapi文档,phalapi wiki,PhalApi文档,phalapi在线文档,phalapi官方文档">

    <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" media="screen">
    <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.css" media="screen">
    <link rel="stylesheet" href="../css/main.css" />
    <link rel="stylesheet" href="../css/loading_bar.css" />
    <link rel="icon" href="http://webtools.qiniudn.com/dog_catch.png" type="image/x-icon" />
    <script src="../js/pace.min.js"></script>
	
	<!-- 代码高亮 -->
	<link rel="stylesheet" href="../css/highlight.min.css">
	<script src="../js/highlight.min.js"></script>
	<script>hljs.initHighlightingOnLoad();</script>
    
</head>

<body>
    <!-- navbar start -->
    <div class="navbar navbar-default navbar-fixed-top" id="mainnav" role="navigation">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand website_name" href="/"><!-- <span>PhalApi</span> --><strong>PhalApi</strong></a>
            </div>
            <div class="navbar-collapse collapse" aria-expanded="false" style="height: 1px;">
                <ul class="nav navbar-nav navbar-right" id="nav_bar">
                    <li><a href="/"><span class="glyphicon glyphicon-home"></span></a></li>
                    <li><a href="http://qa.phalapi.net/">社区</a></li>
                    <li><a href="/download.html">下载</a></li>
                    <li class="active"><a href="/wikis/">文档</a></li>
                    <li><a href="http://www.ituring.com.cn/book/2405">书籍</a></li>
                    <li><a href="http://demo.phalapi.net/">体验</a></li>
                    <li><a href="/about.html">关于</a></li>
                    <li><a href="/donate.html">贡献</a></li>
                    <li><a href="/wikis/en/">English</a></li>
                </ul>
            </div>
            <!--/.nav-collapse -->
        </div>
    </div>
    <!-- navbar end -->


    <div id="content">
        <div class="container">
            <div class="row row-md-flex row-md-flex-wrap">
                <h4><a href="/wikis/%5B1.21%5D-%E7%B1%BB%E7%9A%84%E8%87%AA%E5%8A%A8%E5%8A%A0%E8%BD%BD%EF%BC%9A%E9%81%B5%E5%BE%AAPEAR%E5%8C%85%E7%9A%84%E5%91%BD%E5%90%8D%E8%A7%84%E8%8C%83.html">上一章</a>   <a href="/wikis/">文档首页</a>   <a href="/wikis/%5B1.23%5D-%E8%AF%B7%E6%B1%82%E5%92%8C%E5%93%8D%E5%BA%94%EF%BC%9AGET%E5%92%8CPOST%E4%B8%A4%E8%80%85%E7%9A%86%E5%8F%AF%E5%BE%97%E5%8F%8A%E8%B6%85%E8%B6%8AJSON%E6%A0%BC%E5%BC%8F%E8%BF%94%E5%9B%9E.html">下一章</a></h4>
<hr />
<h2>1.22.1 过滤器服务</h2>
<p>正如我们核心思想DI里面所说的，我们把后台很多功能资源都称为服务，所以在PhalApi框架中我们已经系统规定了<strong>DI()-&gt;filter</strong>为过滤器服务，以实现接口请求时的一些拦截操作，一如现在要说明的签名验证。  </p>
<p>在接口进行初始化时，会自动调用已注册的过滤器服务 <strong>DI()-&gt;filter</strong> ，关键的代码如下：</p>
<pre><code class="language-php">//$vim ./PhalApi/PhalApi/Api.php
    public function init()
    {
        $this-&gt;createMemberValue();

        $this-&gt;filterCheck();

        $this-&gt;checkStatus();
    }

    protected function filterCheck()
    {
        $filter = DI()-&gt;filter;

        if (isset($filter)) {
            $filter-&gt;check();
        }
    }</code></pre>
<h3>默认直接可用的接口验证</h3>
<p>基于很多同学对接口签名验证比较陌生，从框架推出签名验证以来，很长一段时间内，很多同学对于如何实现一个接口签名依然不知如何下手。<br />
为了给大家更好的便利性，我们提供了一个基本版的接口验证服务。  </p>
<p>主要是基于md5进行的签名生成，这个只能作为一般性的参考。大家可以在此基础上进行调整延伸。  </p>
<p>默认情况下，可以去掉注释开启使用PhalApi_Filter_SimpleMD5进行接口验证，即：  </p>
<pre><code class="language-php">//签名验证服务
DI()-&gt;filter = 'PhalApi_Filter_SimpleMD5';</code></pre>
<p>其验签的算法如下（如注释所示）：  </p>
<pre><code class="language-php">1、排除签名参数（默认是sign）
2、将剩下的全部参数，按参数名字进行字典排序
3、将排序好的参数，全部用字符串拼接起来
4、进行md5运算</code></pre>
<p>以下面的示例参数为例，即：  </p>
<pre><code>1、排除签名参数（默认是sign）
?service=Default.Index&amp;username=dogstar

2、将剩下的全部参数，按参数名字进行字典排序
service=Default.Index
username=dogstar

3、将排序好的参数，全部用字符串拼接起来
"Default.Indexdogstar" = "Default.Index" + "dogstar"

4、进行md5运算
sign = 35321cc43cfc1e4008bf6f1bf9b7e3b8 = md5("Default.Indexdogstar")

5、请求时，加上签名参数
?service=Default.Index&amp;username=dogstar&amp;sign=35321cc43cfc1e4008bf6f1bf9b7e3b8</code></pre>
<p>下面是两个调用示例，错误请求下（即签名失败）：  </p>
<pre><code>http://localhost/phalapi/Public/demo/?service=Default.Index&amp;username=dogstar

返回：
{
    "ret": 406,
    "data": [],
    "msg": "非法请求：签名错误"
}</code></pre>
<blockquote>
<p>温馨提示：<br />
签名错误情况下，可以查看日记获得正确的sign，如：  </p>
</blockquote>
<pre><code>2015-10-23 23:16:16|DEBUG|Wrong Sign|{"needSign":"35321cc43cfc1e4008bf6f1bf9b7e3b8"}</code></pre>
<p>正常请求下（带sign签名）：  </p>
<pre><code>http://localhost/phalapi/public/demo/?service=Default.Index&amp;username=dogstar&amp;sign=35321cc43cfc1e4008bf6f1bf9b7e3b8</code></pre>
<p>如果不想使用sign作为关键的签名参数，可以在注册时指定，如使用缩写s：  </p>
<pre><code class="language-php">DI()-&gt;filter = new PhalApi_Filter_SimpleMD5('s');</code></pre>
<h2>1.22.2 微信签名示例</h2>
<p>所以，如果我们需要实现签名验证，只需要简单的两步即可：  </p>
<ul>
<li>1、实现过滤器接口 <strong>PhalApi_Filter::check()</strong>；</li>
<li>2、注册过滤器服务 <strong>DI()-&gt;filter</strong>；</li>
</ul>
<p>下面以大家熟悉的 <a href="http://mp.weixin.qq.com/wiki/17/2d4265491f12608cd170a95559800f2d.html">微信验签</a> 为例，进行示例说明。</p>
<h3>(1)实现过滤器接口 PhalApi_Filter::check()</h3>
<p>通常我们约定返回ret = 402表示验证失败，所以当签名失败时，我们可以返回ret = 402以告知客户端签名不对。根据微信的检验signature的PHP示例代码，我们可以快速实现自定义签名规则，如：</p>
<pre><code class="language-php">//$ vim ./Demo/Common/SignFilter.php 
&lt;?php

class Common_SignFilter implements PhalApi_Filter
{
    public function check()
    {
        $signature = DI()-&gt;request-&gt;get('signature');
        $timestamp = DI()-&gt;request-&gt;get('timestamp');
        $nonce = DI()-&gt;request-&gt;get('nonce');  

        $token = 'Your Token Here ...';
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode( $tmpArr );
        $tmpStr = sha1( $tmpStr );

        if ($tmpStr != $signature) {
            throw new PhalApi_Exception_BadRequest('wrong sign', 1);
        }
    }
}</code></pre>
<h3>(2)注册过滤器服务 DI()-&gt;filter</h3>
<p>随后，我们只需要再简单地注册一下过滤器服务即可，在init.php初始化文件最后追加：</p>
<pre><code class="language-php">//$ vim ./Public/init.php 
//签名验证服务
DI()-&gt;filter = 'Common_SignFilter';</code></pre>
<h3>(3)运行效果</h3>
<p>当我们再次请求接口时，如默认的服务：/demo/?service=Default.Index，即会出现以下的错误：
<img src="http://webtools.qiniudn.com/20150411005257_2bf7d70e9a3a82cae524c03e0fcc6662" alt="apic" />   </p>
<p>即：</p>
<pre><code>{
    "ret": 401,
    "data": [
    ],
    "msg": "非法请求：wrong sign"
}</code></pre>
<p>如果符合接口签名的验证，则会正常返回我们熟悉的内容，如：</p>
<pre><code>/demo/?service=Default.Index&amp;signature=b75e0a1b574d4e111a1d6ed3c9cfbe2ccdc09404&amp;timestamp=123&amp;nonce=123</code></pre>
<p>会返回：</p>
<pre><code class="language-php">{
    "ret": 200,
    "data": {
        "title": "Default Api",
        "content": "PHPer您好，欢迎使用PhalApi！",
        "version": "1.1.0",
        "time": 1423055188
    },
    "msg": ""
}</code></pre>
<h2>1.22.3 特殊的场景</h2>
<h3>(1)个别接口不需要验签？</h3>
<p>在注册好统一的接口验签的过滤器拦截服务后，是会存在这样一种情况：即个别的接口不需要签名。  </p>
<p>而这种情况，我们也是有考虑到的。所以在提供了公共的功能的情况下，我们是可以快速灵活地进行定制化和扩展。  </p>
<p>当我们个别的接口不需要签名验证时，只需要简单地在接口子类里面重定义过滤器的检测即可，如在我们熟悉的默认服务器取消签名验证：</p>
<pre><code class="language-php">//vim ./Demo/Api/Default.php 
class Api_Default extends PhalApi_Api
{
    //....

    protected function filterCheck()
    {
    }
}</code></pre>
<h2>1.22.4 接口服务白名单配置</h2>
<p>除了可以像上面这样编码实现，排除个别接口服务的接口验证，还可以使用接口服务白名单配置，通过框架自身实现对指定配置的接口服务排除。即调用的接口服务，如果配置了白名单，则不调用过滤器。  </p>
<p>接口服务白名单配置是：<code>app.service_whitelist</code>，即配置文件<code>./Config/app.php</code>里面的<code>service_whitelist</code>配置，其默认值是：  </p>
<pre><code class="language-php">    'service_whitelist' =&gt; array(
        'Default.Index',
    ),</code></pre>
<p>如源代码里的注释所示，配置的格式有以下四种。  </p>
<table class="table table-bordered">
<thead>
<tr>
<th>类型</th>
<th>配置格式</th>
<th>匹配规则</th>
<th>示例及说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>全部</td>
<td><code>*.*</code></td>
<td>匹配全部接口服务（慎用！）</td>
<td>如果配置了此规则，即全部的接口服务都不触发过滤器。</td>
</tr>
<tr>
<td>方法通配</td>
<td><code>Default.*</code></td>
<td>匹配某个类的任何方法</td>
<td>即Api_Default接口类的全部方法</td>
</tr>
<tr>
<td>类通配</td>
<td><code>*.Index</code></td>
<td>匹配全部接口类的某个方法</td>
<td>即全部接口类的Index方法</td>
</tr>
<tr>
<td>具体匹配</td>
<td><code>Default.Index</code></td>
<td>匹配指定某个接口服务</td>
<td>即Api_Default::Index()</td>
</tr>
</tbody>
</table>
<p>如果有多个生效的规则，按短路判断原则，即有任何一个白名单规则匹配后就跳过验证，不触发过滤器。  </p>
<p>以下是更多的示例：  </p>
<pre><code class="language-php">    'service_whitelist' =&gt; array(
        '*.Index',           // 全部的Index方法
        'Test.*',            // Api_Test的全部方法
        'User.GetBaseInfo',  // Api_User::GetBaseInfo()方法
    ),</code></pre>
<p>配置好上面的白名单后，以下这些接口服务全部不会触发过滤器：  </p>
<pre><code>// 全部的Index方法
?service=Default.Index
?service=User.Index

// Api_Test的全部方法
?service=Test.DoSth
?service=Test.Hello
?service=Test.GOGOGO

// Api_User::GetBaseInfo()方法
?service=User.GetBaseInfo</code></pre>
<blockquote>
<p>温馨揭示：以上白名单配置，需要PhalApi 1.4.0及以上版本，方可支持。</p>
</blockquote>
<h2>1.22.5 更好地建议</h2>
<p>通常关于接口签名这块，我们还需要：  </p>
<ul>
<li>
<p>1、为不同的接入方定义不同的密钥和私钥；</p>
</li>
<li>
<p>2、如果业务需要，为各个接口、各个接入方分配调用权限；</p>
</li>
<li>3、统一签名参数的规则，可以配置在./Config/app.php中的，如上面的签名需要的参数，我们可以追加统一的参数规则：
<pre><code class="language-php">/**
 * 应用接口层的统一参数
 */
'apiCommonRules' =&gt; array(
    'signature' =&gt; array('name' =&gt; 'signature', 'require' =&gt; true),
    'timestamp' =&gt; array('name' =&gt; 'timestamp', 'require' =&gt; true),
    'nonce' =&gt; array('name' =&gt; 'nonce', 'require' =&gt; true),
),</code></pre></li>
</ul>
<hr />
<h4><a href="/wikis/%5B1.21%5D-%E7%B1%BB%E7%9A%84%E8%87%AA%E5%8A%A8%E5%8A%A0%E8%BD%BD%EF%BC%9A%E9%81%B5%E5%BE%AAPEAR%E5%8C%85%E7%9A%84%E5%91%BD%E5%90%8D%E8%A7%84%E8%8C%83.html">上一章</a>   <a href="/wikis/">文档首页</a>   <a href="/wikis/%5B1.23%5D-%E8%AF%B7%E6%B1%82%E5%92%8C%E5%93%8D%E5%BA%94%EF%BC%9AGET%E5%92%8CPOST%E4%B8%A4%E8%80%85%E7%9A%86%E5%8F%AF%E5%BE%97%E5%8F%8A%E8%B6%85%E8%B6%8AJSON%E6%A0%BC%E5%BC%8F%E8%BF%94%E5%9B%9E.html">下一章</a></h4><div style="float: left">
<h4>
<a href="http://qa.phalapi.net/">还有疑问？欢迎到社区提问！</a>
</h4>
</div>
            </div>
        </div>
    </div>

 <!-- 广告位 -->
<div class="grid-wrapper desktop-only">
	<p align="center">
		<a href="http://7xiz2f.com1.z0.glb.clouddn.com/%E6%88%91%E7%9A%84%E5%90%8D%E5%AD%97%E5%8F%AB%EF%BC%9A%CF%80%E6%A1%86%E6%9E%B6%20-%20PhalApi%202016%E5%B9%B4%E5%BA%A6%E5%BC%80%E6%BA%90%E6%80%BB%E7%BB%93%20-%20%E5%AE%98%E6%96%B9%E5%87%BA%E5%93%81.pdf" target="blank"><img width="950" height="100" src="http://7xiz2f.com1.z0.glb.clouddn.com/ad_20170104.png"></a>
		<a href="http://www.itran.cc/" target="blank"><img width="950" height="100" src="http://7xslqv.com1.z0.glb.clouddn.com/images/9/69/b88bc92455dc1239f9a5bf8d72929.png"></a>
		<a href="http://web-tools.phalapi.net/" target="blank"><img width="950" height="100" src="http://7xslqv.com1.z0.glb.clouddn.com/images/7/d0/88f523566c482296aecc43d185ca2.png"></a>
	</p>
</div>

        <!-- footer -->
        <div class="footer">
            <div class="link container ">
                <div class=" col-xs-7 col-sm-7 col-md-7 col-lg-7 ">
                    <h3>PhalApi (π框架)</h3>
                    <p>一个轻量级PHP开源接口框架，专注于接口服务开发，支持HTTP/SOAP/RPC协议，拥有自动生成的在线文档、多种开发语言的客户端SDK包以及可重用的扩展类库，可用于快速搭建微服务、RESTful接口或Web
                        Services。</p>
                    <!-- <p>:</p> -->
                    <span>友情链接:</span>
                    <a href="https://www.phalapi.net/" target="_blank"> PhalApi </a>
                    <a href="http://www.oschina.net/" target="_blank"> 开源中国 </a>
                    <a href="https://www.itran.cc/" target="_blank">艾翻译</a>
                    <a href="http://www.phalconphp.com/en/" target="_blank">Phalcon</a>
                    <a href="https://phpunit.de/manual/3.7/zh_cn/automating-tests.html" target="_blank">PHPUnit</a>
                    <a href="http://www.thoughtworks.com/cn/" target="_blank">ThoughtWorks</a>
                    <a href="mailto:chanzonghuang@gmail.com">友链交换</a>

                </div>
                <div class=" col-xs-5 col-sm-5 col-md-5 col-lg-5 contact_us ">
                    <h3>联系我们</h3>
                    <a href="https://github.com/phalapi/phalapi"><img src="../images/github.png" alt="github" /></a>
                    <a href="http://weibo.com/p/100808d236e99beb645bfb56ed1c37dde9b8bd?k=phalapi%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6&from=501&_from_=huati_topic"><img src="../images/weibo.png"  alt="weibo" /></a>
                    <a href="http://7xslqv.com1.z0.glb.clouddn.com/images/1/b0/894fde762850cb11250253f7d5ede.png"><img src="../images/zfb.png"  alt="zfb" /></a>
                    <a href="https://jq.qq.com/?_wv=1027&k=4A6reum"><img src="../images/qq.png"  alt="qq" /></a>
					<a href="http://git.oschina.net/dogstar/PhalApi"><img src="../images/git-oschina.png"  alt="git-oschina" /></a>
                </div>
                <div class="cls"></div>
                <div class="text-center copy_right"> ©2015-2017 PhalApi All Rights Reserved. <a href="http://www.miitbeian.gov.cn" target="_blank">粤ICP备15028808号</a>

				<script type="text/javascript">var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_1255326144'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s4.cnzz.com/z_stat.php%3Fid%3D1255326144%26show%3Dpic' type='text/javascript'%3E%3C/script%3E"));</script>
                </div>
            </div>
        </div>
    </div>


    <a href="https://github.com/phalapi"><img id="gitHub_fllow" style="position: fixed; top: 0; right: 0; border: 0;z-index:9999" src="https://camo.githubusercontent.com/652c5b9acfaddf3a9c326fa6bde407b87f7be0f4/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6f72616e67655f6666373630302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png"></a>


    <script src="../js/jquery-1.11.1.min.js"></script>
    <script src="../js/bootstrap.min.js"></script>
</body>

</html>